package com.cwm.common.config;

import com.cwm.common.Enums.ResultEnum;
import com.cwm.common.VO.ResultWrapper;
import com.cwm.common.exception.*;
import com.cwm.common.message.MessageProperties;
import com.cwm.frame4.service.MailService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.HashMap;
import java.util.Map;

/**
 * 全局异常处理
 *
 * @author Bill
 * @version 1.0
 * @since 2021-11-13
 */
@Slf4j
@RequiredArgsConstructor
@ControllerAdvice
public class GlobalException {

    private final MessageProperties messageProperties;

    private final MailService mailService;



    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResultWrapper<Void> noHandlerFoundException(NoHandlerFoundException e) {
        return getError(ResultEnum.NOT_FOUND, e);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultWrapper<Void> methodArgumentNotValidException(MethodArgumentNotValidException e) {
        Map<String, String> errors = new HashMap<>();
        e.getBindingResult().getAllErrors().forEach((error) -> {
            String fieldName = ((FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            errors.put(fieldName, errorMessage);
        });
        String errMsg = errors.toString();
        log.error("[Nexlabel ERR] {}", errMsg);
        return getError(ResultEnum.ARGUMENT_NOT_VALID, errMsg, String.join(", ", errors.values()));
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(AuthorizationException.class)
    public ResultWrapper<Void> authorizationException(AuthorizationException e) {
        return baseException(e);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BizException.class)
    public ResultWrapper<Void> bizException(BizException e) {
        return baseException(e);
    }

    private ResultWrapper<Void> baseException(BaseException e) {
        if (e.getParameters() == null || e.getParameters().isEmpty()) {
            return getErrorWithoutDebugMessage(e.getCode());
        }
        return getErrorWithoutDebugMessage(e.getCode(), e.getParameters().toArray(new String[0]));
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(Throwable.class)
    public ResultWrapper<Void> otherException(Throwable e) {
        log.error("error stack", e);
        try {
            mailService.notifyOperator(e);
        } catch (Throwable exception) {
            log.error("notify error", exception);
        }
        return getError(ResultEnum.BAD_REQUEST, e, e.getMessage());
    }

    private ResultWrapper<Void> getError(ResultEnum ResultEnum, Throwable e) {
        String message = messageProperties.getMessage(ResultEnum);
        log.error("error message: {}", message);
        return ResultWrapper.error(ResultEnum, message, e);
    }

    private ResultWrapper<Void> getError(ResultEnum ResultEnum, Throwable e, String... parameter) {
        String message = messageProperties.getMessage(ResultEnum, parameter);
        log.error("error message: {}", message);
        return ResultWrapper.error(ResultEnum, message, e);
    }

    private ResultWrapper<Void> getError(ResultEnum ResultEnum, String debugMessage, String... parameter) {
        String message = messageProperties.getMessage(ResultEnum, parameter);
        log.error("error message: {}", message);
        return ResultWrapper.error(ResultEnum, message, debugMessage);
    }

    private ResultWrapper<Void> getErrorWithoutDebugMessage(ResultEnum ResultEnum, String... parameter) {
        String message = messageProperties.getMessage(ResultEnum, parameter);
        log.error("error message: {}", message);
        return ResultWrapper.error(ResultEnum, message, "");
    }

    private ResultWrapper<Void> getErrorWithoutDebugMessage(ResultEnum ResultEnum) {
        String message = messageProperties.getMessage(ResultEnum);
        log.error("error message: {}", message);
        return ResultWrapper.error(ResultEnum, message, "");
    }

}
